package com.piece.core.resource.service;

import com.piece.core.framework.constant.*;
import com.piece.core.framework.enums.StatusType;
import com.piece.core.framework.support.dto.UserDetailDTO;
import com.piece.core.framework.util.basic.I18nUtil;
import com.piece.core.framework.util.collection.CollectionUtil;
import com.piece.core.framework.util.security.coder.Base64Coder;
import com.piece.core.web.authority.AuthorityService;
import com.piece.core.web.authority.token.TokenServices;
import com.piece.core.web.cookie.CookieUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;

/**
 * 认证授权相关工具类
 */
@Slf4j
@Component("oAuthAuthorityService")
public class OAuthAuthorityService implements AuthorityService {

    @Resource
    private TokenStore tokenStore;

    @Lazy
    @Resource
    private TokenServices tokenServices;

    @Override
    public String extractToken(HttpServletRequest request) {
        String token = extractHeaderToken(request);
        if (null == token) {
            token = request.getParameter(ParamConstants.TOKEN);
        }
        if (null == token) {
            token = CookieUtil.getCookieValue(request, CookieConstants.SESSION_KEY);
        }

        return token;
    }

    /**
     * 从Header解析token
     */
    public String extractHeaderToken(HttpServletRequest request) {
        Enumeration<String> headers = request.getHeaders(HeaderConstants.Authorization);
        while (headers.hasMoreElements()) {
            String value = headers.nextElement();
            if ((value.startsWith(OAuth2AccessToken.BEARER_TYPE))) {
                String authHeaderValue = value.substring(OAuth2AccessToken.BEARER_TYPE.length() + 1).trim();
                int commaIndex = authHeaderValue.indexOf(',');
                if (commaIndex > 0) {
                    authHeaderValue = authHeaderValue.substring(0, commaIndex);
                }
                return authHeaderValue;
            }
        }
        return null;
    }

    @Override
    public String extractClientId(HttpServletRequest request) {
        return extractClient(request)[0];
    }

    /**
     * 解析clientId:clientSecret
     */
    @Override
    public String[] extractClient(HttpServletRequest request) {
        String header = request.getHeader(HeaderConstants.TENANT);
        if (null == header) {
            header = request.getHeader(HeaderConstants.Authorization);
        }
        if (null == header) {
            return extractParamClient(request);
        }
        return extractHeaderClient(header);
    }

    /**
     * 从Header解析clientId:clientSecret
     */
    public String[] extractHeaderClient(String header) {
        if ((header.startsWith(FrameWorkConstants.BASIC_TYPE))) {
            header = header.substring(FrameWorkConstants.BASIC_TYPE.length()).trim();
        }

        String clientStr = Base64Coder.decode(header);
        String[] clientArr = clientStr.split(":");
        if (clientArr.length != 2) {
            throw new RuntimeException(I18nUtil.message(ExceptionAuthConstants.CLIENT_INVALID));
        }
        return clientArr;
    }

    public String[] extractParamClient(HttpServletRequest request) {
        String clientId = request.getParameter(ParamConstants.CLIENT_ID);
        String clientSecret = request.getParameter(ParamConstants.CLIENT_SECRET);
        if (null == clientId) {
            throw new UnapprovedClientAuthenticationException(I18nUtil.message(ExceptionAuthConstants.CLIENT_INVALID));
        }
        return new String[]{clientId, clientSecret};
    }

    /**
     * 获取登录用户权限
     */
    public Collection getUserAuthorities() {
        Set<String> permissions = new HashSet<>();
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (null != authentication) {
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            for (GrantedAuthority authority : authorities) {
                permissions.add(authority.getAuthority());
            }
        }
        return permissions;
    }

    /**
     * 判断登录用户是否有某个权限
     */
    @Override
    public boolean isPermitted(String... permission) {
        Collection authorities = getUserAuthorities();
        if (authorities.contains(PermissionConstants.ADMIN_PERMISSION)) {
            return true;
        }

        Predicate<String> predicate;
        for (int i = 0; i < permission.length; i++) {
            String regex = convertExpression(permission[i]);

            predicate = s -> s.matches(regex);
            if (authorities.parallelStream().anyMatch(predicate)) {
                return true;
            }
        }
        return false;
    }

    public String convertExpression(String regex) {
        if ("*".equals(regex)) {
            regex = "//*";
        }

        return regex;
    }

    /**
     * 判断用户是否在线
     */
    public String checkUserOnlineState(String clientId, String loginName) {
        Collection<OAuth2AccessToken> tokens = tokenStore.findTokensByClientIdAndUserName(clientId, loginName);
        if (CollectionUtil.isNotEmpty(tokens)) {
            return StatusType.NORMAL.getCode();
        }
        return StatusType.DELETE.getCode();
    }

    /**
     * 强制用户下线
     */
    public boolean offLineUser(String clientId, String loginName) {
        Collection<OAuth2AccessToken> tokens = tokenStore.findTokensByClientIdAndUserName(clientId, loginName);
        if (null != tokens && tokens.size() > 0) {
            for (OAuth2AccessToken kickAccessToken : tokens) {
                if (null != kickAccessToken.getRefreshToken()) {
                    tokenStore.removeRefreshToken(kickAccessToken.getRefreshToken());
                }
                tokenStore.removeAccessToken(kickAccessToken);
            }
            return true;
        }
        return false;
    }

    /**
     * 自动用户登录
     */
    public String autoLogin(String clientId, UserDetailDTO userDetail) {
        OAuth2AccessToken token = tokenServices.getUserTokenInfo(clientId, userDetail);
        return token.getValue();
    }
}
